package main

import (
  "fmt"
  "net/http"
  "time"
  "github.com/labstack/echo/v4"
  jqr "github.com/jinquan0/jqdb/jqmysql"
  uuid "github.com/satori/go.uuid"
  "github.com/elgs/gostrgen"
  "strconv"
  //"crypto/rand" //真随机数
  //"encoding/binary"
  "math/rand" //伪随机数
)

type ST_MysqlConfig struct {
    Host  string `yaml:"host"`
    Port  string `yaml:"port"`
    User  string `yaml:"user"`
    Pass  string `yaml:"pass"`
    Sslca  string `yaml:"sslca"`
    Db  string `yaml:"db"`
}


func GenerateRandomNum(min, max int) int {
  rand.Seed(time.Now().Unix())
  randNum := rand.Intn(max - min) + min
  return randNum
}

/*
func RealRandomNun() int32 {
    var n int32
    binary.Read(rand.Reader, binary.LittleEndian, &n)
    return n
}
*/

func SelectTableById(table string, id int) string {
    e := &jqr.ST_MySQL_Endpoint {
      Host:  GlobalConfig.Mysql.Host,
      Port:  GlobalConfig.Mysql.Port,
      User:  GlobalConfig.Mysql.User,
      Pass:  GlobalConfig.Mysql.Pass,
      Sslca: GlobalConfig.Mysql.Sslca,
      Db:    GlobalConfig.Mysql.Db,
    }
    
    // mysql select reply buffer initialize
    type ST_MySelectFields_1 struct{
        Id      int    `json:"id"`
        Uid     string  `json:"uid"`
        Token   string  `json:"token"`
    }
    var sz int = 2
    any_array := make([]interface{}, sz, sz)
    for i:=0; i < sz; i++ { 
        any_array[i]=new(ST_MySelectFields_1) 
    }

    // connect db
    db := jqr.MyConn(e)
    // construct sql text
    var sqltext string = "select id,uid,token from " + table + " where id=" + strconv.Itoa(id) + ";"
    //MyLog.Info(sqltext)
    // execute sql text
    q:=&jqr.ST_MyCommonQuery {
      Sql: sqltext,
      AnyArray: any_array,
    }
    _,numrow:=jqr.MyCommonSelect(*q, db)

    //MyLog.Info(fmt.Sprintf("total %d rows", numrow))
    var reply string = ""
    for i:=0; i < numrow; i++ {
      //MyLog.Info(fmt.Sprintf("%v", any_array[i]))
      reply = reply + fmt.Sprintf("%v", any_array[i])
    }
    
    // disconnect db
    jqr.MyDisconn(db)

    return reply
}


func Application_Logic(idx int, cnt int, table string, id_range []int) {
    for i:=0; i < cnt; i++ {
      // fmt.Printf("%d\n",GenerateRandomNum(id_range[0], id_range[1]))
      MyLog.Info(SelectTableById(table, GenerateRandomNum(id_range[0], id_range[1])))

      // time.Sleep(1 * time.Second)
      time.Sleep(time.Duration(GlobalConfig.RoutineDelayms) * time.Millisecond)
    }
}

type ST_MyCh struct {
    Routines int
    Count int
    Table string
    IdRange []int
}

var MyChannel chan *ST_MyCh

func Application_Dispatcher() {
    for ;; {
      msg, ok := <-MyChannel
      if !ok {
        MyLog.Info("receive message from channel failed.")
      } else{
        for i:=0; i < msg.Routines; i++ {
          go func(idx int, cnt int, table string, id_range []int) {
            Application_Logic(idx, cnt, table, id_range)
          }(i, msg.Count, msg.Table, msg.IdRange)
        }
      }
    }
}

/*
并发查询
curl -XPOST http://127.0.0.1:8000/api/v1/dbcs -H 'Content-Type: application/json' -d '{"routines": 20, "count": 5000, "table": "test01", "id_range": [1,100]}'
*/
func Handler_ConcurrentSelect(c echo.Context) error {
    type (
      RequestParameter struct {
        Routines int `json:"routines"`
        Count int `json:"count"`
        Table string `json:"table"`
        IdRange []int `json:"id_range"`
      }
    )
    ReqPara := new(RequestParameter)
    if err := c.Bind(ReqPara); err != nil {
      return echo.NewHTTPError(http.StatusBadRequest, err.Error())
    }
    // send message to channel
    Msg := new(ST_MyCh)
    Msg.Routines = ReqPara.Routines
    Msg.Count = ReqPara.Count
    Msg.Table = ReqPara.Table
    Msg.IdRange = ReqPara.IdRange  // Golang数组拷贝赋值, 索引自动对齐
    MyChannel <- Msg

    return c.String(200, "ok")
}


/*
curl -XPOST http://127.0.0.1:8000/api/v1/dbctab -H 'Content-Type: application/json' -d '{"table": "test01"}'
*/
func Handler_CreateTable(c echo.Context) error {
    type (
      RequestParameter struct {
        Table string `json:"table"`
      }
    )
    ReqPara := new(RequestParameter)
    if err := c.Bind(ReqPara); err != nil {
      return echo.NewHTTPError(http.StatusBadRequest, err.Error())
    }

    e := &jqr.ST_MySQL_Endpoint {
      Host:  GlobalConfig.Mysql.Host,
      Port:  GlobalConfig.Mysql.Port,
      User:  GlobalConfig.Mysql.User,
      Pass:  GlobalConfig.Mysql.Pass,
      Sslca: GlobalConfig.Mysql.Sslca,
      Db:    GlobalConfig.Mysql.Db,
    }
    
    sqlstr:="CREATE TABLE IF NOT EXISTS " + ReqPara.Table +
            "(id INT(10) UNSIGNED PRIMARY KEY NOT NULL AUTO_INCREMENT," +
            "uid CHAR(255) NOT NULL," +
            "token CHAR(32) NOT NULL" +
            ") ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;"

    db := jqr.MyConn(e)
    smt, err := db.Prepare(sqlstr)
    if err != nil {
      jqr.MyDisconn(db)
      MyLog.Info(sqlstr, err)
      return c.String(200, "create table failed")
    }
    smt.Exec()
    jqr.MyDisconn(db)
    MyLog.Info(fmt.Sprintf("Create MySQL table '%s' successfully", ReqPara.Table))

    return c.String(200, "ok")
}


/*
curl -XPOST http://127.0.0.1:8000/api/v1/dbitab -H 'Content-Type: application/json' -d '{"table": "test01", "rows": 10}'
*/
func Handler_InsertTable(c echo.Context) error {
    type (
      RequestParameter struct {
        Table string `json:"table"`
        Rows int `json:"rows"`
      }
    )
    ReqPara := new(RequestParameter)
    if err := c.Bind(ReqPara); err != nil {
      return echo.NewHTTPError(http.StatusBadRequest, err.Error())
    }

    e := &jqr.ST_MySQL_Endpoint {
      Host:  GlobalConfig.Mysql.Host,
      Port:  GlobalConfig.Mysql.Port,
      User:  GlobalConfig.Mysql.User,
      Pass:  GlobalConfig.Mysql.Pass,
      Sslca: GlobalConfig.Mysql.Sslca,
      Db:    GlobalConfig.Mysql.Db,
    }
    
    type ST_MyTabInsert struct {
        Uid string
        Token string
    }

    db := jqr.MyConn(e)

    for i:=0; i < ReqPara.Rows; i++ {
      // charsToGenerate, charSet, includes, excludes
      randstr, err := gostrgen.RandGen(16, gostrgen.Lower | gostrgen.Digit, "^&%$#*@!~-+", "oO0liI1")
      if err != nil {
        MyLog.Error("token", err)
        randstr = "null"
      }

      data := ST_MyTabInsert {
        Uid: fmt.Sprintf("%s", uuid.NewV4()),
        Token: randstr,
      }
      full_sql := fmt.Sprintf("INSERT IGNORE INTO %s(`id`, `uid`, `token`) VALUES (null,%s,%s);", ReqPara.Table, data.Uid, data.Token )
      exec_sql := "INSERT IGNORE INTO "+ReqPara.Table+"(`id`, `uid`, `token`) VALUES (null,?,?)"
      MyLog.Info(full_sql)
      // id: AUTO_INCREMENT
      jqr.MyInsert( db, exec_sql, data , false) 
      
    }

    jqr.MyDisconn(db)

    return c.String(200, "ok")
}

/*
curl -XPOST http://127.0.0.1:8000/api/v1/dbstab -H 'Content-Type: application/json' -d '{"table": "test01", "id": [1,2,3,4,5,6,7,8]}'
*/
func Handler_SelectTable(c echo.Context) error {
    type (
      RequestParameter struct {
        Table string `json:"table"`
        Id []int `json:"id"`
      }
    )
    ReqPara := new(RequestParameter)
    if err := c.Bind(ReqPara); err != nil {
      return echo.NewHTTPError(http.StatusBadRequest, err.Error())
    }

    // array size
    // fmt.Println(len(ReqPara.Id))

    e := &jqr.ST_MySQL_Endpoint {
      Host:  GlobalConfig.Mysql.Host,
      Port:  GlobalConfig.Mysql.Port,
      User:  GlobalConfig.Mysql.User,
      Pass:  GlobalConfig.Mysql.Pass,
      Sslca: GlobalConfig.Mysql.Sslca,
      Db:    GlobalConfig.Mysql.Db,
    }
    
    // mysql select reply buffer initialize
    type ST_MySelectFields_1 struct{
        Id      int    `json:"id"`
        Uid     string  `json:"uid"`
        Token   string  `json:"token"`
    }
    var sz int = 100
    any_array := make([]interface{}, sz, sz)
    for i:=0; i < sz; i++ { 
        any_array[i]=new(ST_MySelectFields_1) 
    }

    // connect db
    db := jqr.MyConn(e)
    // construct sql text
    var sqltext string = "select id,uid,token from " + ReqPara.Table + " where "
    for i:=0; i < len(ReqPara.Id); i++ {
      sqltext = sqltext + "id=" + strconv.Itoa(ReqPara.Id[i])
      if i < (len(ReqPara.Id)-1) {
        sqltext = sqltext + " or "
      } else {
        sqltext = sqltext + ";"
      }
    }
    MyLog.Info(sqltext)
    // execute sql text
    q:=&jqr.ST_MyCommonQuery {
      Sql: sqltext,
      AnyArray: any_array,
    }
    _,numrow:=jqr.MyCommonSelect(*q, db)

    MyLog.Info(fmt.Sprintf("total %d rows", numrow))
    for i:=0; i < numrow; i++ {
      MyLog.Info(fmt.Sprintf("%v", any_array[i]))
    }
    
    // disconnect db
    jqr.MyDisconn(db)

    return c.String(200, "ok")
}